package com.lc.servlet;

import com.lc.annotation.*;
import com.lc.hander.Hander;
import com.lc.util.MyRequestMethod;
import com.lc.viewResolve.View;
import com.lc.viewResolve.ViewResolve;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.*;

/**
 * Created by lichao on 2017/12/29.
 */
public class MyDispacherServlet extends HttpServlet{

    private List<String> packageNames = new ArrayList<String>();

    private Map<String,Object> instances = new HashMap<String , Object>();

    private Map<String ,Map<MyRequestMethod , Hander>> handerMappers = new HashMap<String , Map<MyRequestMethod , Hander>>();

    private List<ViewResolve> viewResolves = new ArrayList<ViewResolve>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        //1.加载配置文件

        //2.扫描包中的文件,获取文件名
        scanPackage("com.lc");
        System.out.println(packageNames);
        //3.获取包中的实例 , 并建立映射关系
        try {
            initInstance();
        }catch(Exception e){
            e.printStackTrace();
        }
        //4.ioc注入
        initIoc();
        //5.装载视图解析器
        initViewResolve();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doDispacher(req , resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doDispacher(req , resp);
    }

    private void doDispacher(HttpServletRequest req, HttpServletResponse resp){
        Map<String , String[]> params = new HashMap<>();
        String url = req.getRequestURI().substring(req.getContextPath().length()+1);
        MyRequestMethod type = getParamsByMethodType(req , params);
        Map<MyRequestMethod , Hander> map = handerMappers.get(url);
        if(map!=null&&map.get(type)!=null){
            Hander hander = map.get(type);
            Method method = hander.getMethod();
            Parameter[] myRequestParams = method.getParameters();
            Object[] args = new Object[myRequestParams.length];
            for(int i = 0 ; i<myRequestParams.length ; i++){
                if(myRequestParams[i].isAnnotationPresent(MyRequestParam.class)) {
                    args[i] = params.get(myRequestParams[i].getAnnotation(MyRequestParam.class).value())[0];
                }else{
                    args[i] = params.get("arg"+i)[0];
                }
            }
            try {
                Object result = method.invoke(instances.get(hander.getInstanceName()),args);
                View view = getViewName(result);
                resolveView(req,resp,view);

            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    private MyRequestMethod getParamsByMethodType(HttpServletRequest req , Map<String , String[]> params){
        MyRequestMethod type = MyRequestMethod.GET;
        String reqType = req.getMethod();
        if(reqType.equals("GET")){
            String query = req.getQueryString();
            if(query!=null&&!query.equals("")) {
                String[] querys = query.split("&");
                for (String q : querys) {
                    String[] param = q.split("=");
                    params.put(param[0], param[1].split("&"));
                }
            }
        }else if(reqType.equals("POST")){
            type = MyRequestMethod.POST;
            params.putAll(req.getParameterMap());
        }
        return type;
    }

    private View getViewName(Object result){
        View view = new View();
        if(result instanceof View){
            view = (View) result;
        }else {
            view.setViewName(result.toString());
        }
        return view;
    }

    private void scanPackage(String path){
        URL url = this.getClass().getClassLoader().getResource("/"+ path.replaceAll("\\.","/"));
        String urlFile = url.getFile();
        File file = new File(urlFile);
        String[] fielNames = file.list();
        for (String fileName:fielNames) {
            File f = new File(urlFile + fileName);
            if(f.isDirectory()){
                scanPackage(path + "." + f.getName());
            }else{
                packageNames.add(path + "." + f.getName().substring(0,f.getName().lastIndexOf(".class")));
            }
        }
    }
    private void initInstance() throws Exception{
        if(packageNames.size() <= 0 )return;
        for(String packageName:packageNames){
            Class clazz = Class.forName(packageName);
            if(clazz.isAnnotationPresent(MyController.class)){
                MyController myController = (MyController)clazz.getAnnotation(MyController.class);
                String name  = myController.value();
                name = name.equals("")?(clazz.getName().substring(clazz.getName().lastIndexOf(".")+1,clazz.getName().length())):name;
                instances.put(name, clazz.newInstance());
                initHanderMapper(clazz,name);
            }else if(clazz.isAnnotationPresent(MyService.class)){
                MyService myService = (MyService)clazz.getAnnotation(MyService.class);
                String name  = myService.value();
                name = name.equals("")?(clazz.getName().substring(clazz.getName().lastIndexOf(".")+1,clazz.getName().length())):name;
                instances.put(name , clazz.newInstance());
                initHanderMapper(clazz,name);
            }
        }
    }

    private void initHanderMapper(Class clazz,String name)throws Exception{
        String pathLeft = "";
        if(clazz.isAnnotationPresent(MyRequestMapping.class)){
            MyRequestMapping myRequestMapping = (MyRequestMapping)clazz.getAnnotation(MyRequestMapping.class);
            pathLeft = myRequestMapping.value();
        }
        for(Method method : clazz.getDeclaredMethods()){
            if(method.isAnnotationPresent(MyRequestMapping.class)){
                String path = "";
                Hander hander = new Hander();
                MyRequestMapping myRequestMapping = method.getAnnotation(MyRequestMapping.class);
                if(!myRequestMapping.value().equals("")) {
                    path = (pathLeft + "/"+ myRequestMapping.value()).replaceAll("/+","/");
                }
                hander.setInstanceName(name);
                hander.setMethod(method);
                if(handerMappers.get(path)!=null){
                    handerMappers.get(path).put(myRequestMapping.method(),hander);
                }else{
                    Map map = new HashMap<MyRequestMethod , Hander>();
                    map.put(myRequestMapping.method(),hander);
                    handerMappers.put(path , map);
                }
            }
        }
    }

    private void initIoc(){
        if(instances.isEmpty())return;
        for(Map.Entry<String, Object> entry : instances.entrySet()){
            Object o = entry.getValue();
            Field[] fields = o.getClass().getDeclaredFields();
            for(Field field : fields){
                field.setAccessible(true);
                if(field.isAnnotationPresent(MyResource.class)){
                    MyResource myResource = field.getAnnotation(MyResource.class);
                    String name = myResource.name();
                    if(myResource.name().equals("")){name = field.getName();}
                    Object m = instances.get(name);
                    if(m==null || m.equals(o))continue;//按类型修改需要修改instances的类型
                    try {
                        field.set(o, m);
                    }catch(IllegalAccessException e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private void initViewResolve(){
        if(instances.isEmpty())return;
        for(Object o:instances.values()){
            if(o instanceof ViewResolve){
                viewResolves.add((ViewResolve) o);
            }
        }
    }

    private void resolveView(HttpServletRequest request, HttpServletResponse response,View view){
        for(ViewResolve viewResolve:viewResolves){
            if(viewResolve.resolve(request,response,view))break;
        }
    }
}
